/*
 * FILE: get_symbol.c
 *
 * find symbol
 *
 * step1: g_fsym_str中新增需要寻找的变量/函数
 * step2: fsym_type中新增对应类型 *注：与g_fsym_str中的每一项匹配
 * step3: 新增变量/函数定义 *注：命名为'orig_'+'原始参数' 
 			    如：原始名为sys_call_table，自定义为orig_call_table  
 * step4: covert_func()中符号获取 
 *
 * eg: 新增寻找int kernel_func();
 *	1,g_fsym_str中最后一项新增一行: "kernel_func",
 *	2,fsym_type在FSYM_COUNT上新增一行: KERNEL_FUNC,
 *	3,增加变量: int (*orig_kernel_func)();
 *	4,covert_func()中增加： orig_kernel_func = g_zds_fsym[KERNEL_FUNC];
 */



#include "get_symbol.h"

char *g_fsym_str[] = {
	"security_add_hooks",
	"security_hook_heads",
	"sys_call_table",
};

enum fsym_type{
	SECURITY_ADD_HOOKS = 0,
	SECURITY_HOOK_HEADS,
	SYS_CALL_TABLE,
	FSYM_COUNT
};
void *g_zds_fsym[FSYM_COUNT];
void (*orig_security_add_hooks)(struct security_hook_list *hooks, int count,char *lsm);
struct security_hook_heads *orig_security_hook_heads;
unsigned long *orig_sys_call_table;
static void convert_func(void){
	orig_security_add_hooks = g_zds_fsym[SECURITY_ADD_HOOKS];
	printk("%lx\n", g_zds_fsym[SECURITY_ADD_HOOKS]);
	orig_security_hook_heads = g_zds_fsym[SECURITY_HOOK_HEADS];
	printk("%lx\n", g_zds_fsym[SECURITY_HOOK_HEADS]);
	orig_sys_call_table = g_zds_fsym[SYS_CALL_TABLE];
	printk("%lx\n", g_zds_fsym[SYS_CALL_TABLE]);
}

static void * check_function_address(void *ptr, char *symbol)
{
//#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
        static char buf[KSYM_SYMBOL_LEN];
        const int len = strlen(symbol);
//#endif

        if (!ptr) {
                // -- printk(KERN_ERR "Can't resolve %s().\n", symbol);
                return NULL;
        }
//#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
        snprintf(buf, sizeof(buf), "%pS", ptr);
        if (strncmp(buf, symbol, len) || strncmp(buf + len, "+0x0/", 5)) {
                // -- printk(KERN_ERR "Guessed %s is %s\n", symbol, buf);
                return NULL;
        }
//#else
        // -- printk(KERN_INFO "%s=%p\n", symbol, ptr);
//#endif
        return ptr;
}


int fsym_init(void){
	int i;
	void *sym;
	
	for(i = 0; i < ARRAY_SIZE(g_fsym_str); i++){
		sym = get_symbol(g_fsym_str[i]);
		if(likely(sym))
			g_zds_fsym[i] = sym;
		else
			return -EFAULT;

		sym = check_function_address(g_zds_fsym[i], g_fsym_str[i]);
		if(!sym)
			return -EFAULT;
	}

	convert_func();
	return 0;
}


static int find_symbol_callback(struct kernsym *sym, const char *name, struct module *mod,
                                unsigned long addr)
{
	if (sym->found) {
		sym->end_addr = (unsigned long *)addr;
		return 1;
	}

	// this symbol was found. the next callback will be the address of the next symbol
	if (name && sym->name && !strcmp(name, sym->name)) {
		sym->addr = (unsigned long *)addr;
		sym->found = true;
	}

	return 0;
}

int find_symbol_address(struct kernsym *sym, const char *symbol_name)
{
	int ret;

	sym->name = (char *)symbol_name;
	sym->found = 0;

	ret = kallsyms_on_each_symbol((void *)find_symbol_callback, sym);
	if (!ret)
		return -EFAULT;

	sym->size = sym->end_addr - sym->addr;
	sym->new_size = sym->size;
	sym->run = sym->addr;

	return 0;
}


void * get_symbol(const char * symbol)
{
	void * retval = NULL;
	#ifndef __USING_KALLSYMS_LOOKUP_NAME__
		int ret = 0;
		struct kernsym sym;
	#endif

	#ifdef __USING_KALLSYMS_LOOKUP_NAME__
		retval = (void *)kallsyms_lookup_name(symbol);
	#else
		memset(&sym, 0, sizeof(sym));
		ret = find_symbol_address(&sym, symbol);
		if (ret){
			goto DONE;
		}
		retval = sym.run;
	#endif
DONE:
	if (retval != NULL)
		printk("get symbol[%s-%lx]\n", symbol, retval);
	return retval;
}


